ShowTable of Contents
Part 1: Server Side Control
The Sametime Proxy SDK can be used to access many of the Sametime features, but it has edge use-cases that, when exploited properly, can make the web experience much more satisfying. This series of articles, along with the associated sample code, illustrates how to create the customer-side interaction, the service agent application, and the bot application running on a server.
How it works
Much of the complexity of the solution is handled automatically by the usual Sametime functionality. This diagram shows what happens:

When the customer clicks on the button, it informs the bot that she requires attention. This information is forwarded to the agent who initiates the chat with the customer. Optionally the agent can call a supervisor into the chat conversation.
The Parts
From the diagram, you can see that this sample solution contains three parts:
A customer-facing web application
A service-agent web application
A servlet/bot that acts to connect the above
Programming these parts requires an understanding of both the Sametime Proxy SDK and the Sametime Java server toolkit. We'll explain the choices made, but if you are not familiar with these, you should download the Sametime Toolkit from the link below, and refer to its documentation.
Of course, we need some predefined data items in the system, in particular the Bot, the group of agents and the group of supervisors. In our example case, we have:

where the bot has the username servicebot, and we have a group of agents called Support staff, and a group containing supervisors named, unsurprisingly, Supervisors. These three are the main functional participants in our solution.
The Servlet
The servlet really has two parts:
- a class that is a Java EE HttpServlet
- a class that acts as a Sametime Bot, managing the connection to the Sametime Community server, and a helper class that acts as part of this Sametime Bot, managing the customer support agents and their availability
From the point of view of Sametime, the bot is just another user: it logs in, creates a watchlist to get user status updates, sends and receives messages, etc.
Obviously we have to provide a number of parameters in order for this to work:
- server - the fully-qualified domain name of the Sametime Community Server to which the bot connects.
- username – the name used by the bot to log in to Sametime. This user must be in the directory, i.e. in LDAP or Active Directory or whatever you usually use as your directory service.
- password – the password for the bot's user
- agents – the name of the Sametime public group that contains the names of the agents, i.e. the customer support agents are all members of this group. This allows easy maintenance of the group, where you can add or remove agents as required.
These parameters could be provided in many ways, such as in the servlet's initialization parameters, in an XML file, or in a properties file. For the sake of simplicity, the sample code uses the initialization parameters. In fact, much of the code is deliberately written in a quite simple style, in order to more clearly illustrate concepts.
The initialization parameters are stored in the application's web.xml file, in the servlet definition.
Listing 1: The servlet definition in web.xml
<servlet>
<description>
This takes information about a customer over a REST call,
and forwards the request to a customer service agent, over the
Sametime network.
</description>
<display-name>ServiceServlet</display-name>
<servlet-name>ServiceServlet</servlet-name>
<servlet-class>com.ibm.sametime.sample.ServiceServlet</servlet-class>
<init-param>
<description>Sametime Community Server name</description>
<param-name>server</param-name>
<param-value>dubxpcvm675.mul.ie.ibm.com</param-value>
</init-param>
<init-param>
<description>Sametime login name</description>
<param-name>name</param-name>
<param-value>servicebot</param-value>
</init-param>
<init-param>
<description>Sametime password</description>
<param-name>password</param-name>
<param-value>password</param-value>
</init-param>
<init-param>
<description>Support agents' group name</description>
<param-name>agents</param-name>
<param-value>Support staff</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
To ensure that the init method is called, it is necessary to specify load-on-startup in the web.xml, as you can see above.
Of course, we also have to ensure that we can correctly address the servlet, so the servlet-mapping in web.xml defines how to reach the servlet:
Listing 2: The servlet mapping in web.xml
<servlet-mapping>
<servlet-name>ServiceServlet</servlet-name>
<url-pattern>/ServiceServlet</url-pattern>
</servlet-mapping>
The HttpServlet class
Like any other servlet class, this manages the actual web connectivity of the server. Its function is to respond to a customer's request for assistance which means that it has two functions: it must initialize the Sametime functionality when loaded, by providing an init() function, and it must accept customer requests in doPost(), passing the details on to an available agent.
Listing 3: The servlet init() method.
servicebot = new DispatcherBot(getServletConfig().getInitParameter("server"),
getServletConfig().getInitParameter("name"),
getServletConfig().getInitParameter("password"),
getServletConfig().getInitParameter("agents"));
This code used the init parameters from the web.xml. They could also be provided by another method, such as a properties file or an external xml configuration file
When a customer requests to chat, the web page will gather some information and forward it to the servlet, where the details are extracted from the request, and then forwarded to the bot code to request that an agent start a chat. The data are sent to the server in the request body of a POST, in order to provide as much security as possible. Note that since this solution is simple, it has no protections against attack.
The actual POST request sends along the information picked up from the screen. Typically such information will include such things as the customer's name, e-mail address and telephone number, along with a short explanation on why the customer is contacting the agent. In order to uniquely identify the customer as a Sametime user, a unique ID is also sent as one of the parameters. We'll cover this ID later in the section that addresses the customer web pages.
Listing 4: the doPost() method.
// Get the parameters
String id = req.getParameter("id");
String user = req.getParameter("user");
String email = req.getParameter("email");
String phone = req.getParameter("phone");
String reason = req.getParameter("reason");
// Respond to the client
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
// The only required parameter is the username
if (null == user || user.length() == 0) {
out.println("Please fill in the required fields.");
} else if (servicebot.isLoggedIn()) {
// If the bot's available display on the web page
out.println("Request received. Locating agent ...");
servicebot.alertPartner(id, user, email, phone, reason);
} else {
// The bot hasn't initialized – it's an error!
out.println("Sametime bot not available. Please try again later.");
log.fine("DispatcherBot not logged in.");
}
The Sametime Bot – the DispatcherBot
This class manages most of the interaction with Sametime. While it has a number of methods, two in particular are interesting in that they manage the interactions in which we're really interested. The other methods are required by the interfaces and can be considered overhead, and you can read more on these in the Sametime Toolkit documentation. You can add more functionality to these if you want to, but we'll focus on three: the constructor, the loggedIn() and the alertPartner() methods.
The constructor uses the parameters extracted during the servlet's init() method to establish a connection to Sametime, logging in using the supplied username and password.
Listing 5: the DispatcherBot constructor
public DispatcherBot(String serverName, String userId,
String password, String agentsGroup) {
// We need all four parameters
if (null != serverName && null != userId &&
null != password && null != agentsGroup) {
m_groupname = agentsGroup;
try {
m_stsession = new STSession(this.getClass().getName() + this);
} catch (DuplicateObjectException e) {
e.printStackTrace();
return;
}
// Set up the session
m_stsession.loadSemanticComponents();
m_stsession.start();
// get the Community service
m_commService =
(CommunityService) m_stsession.getCompApi(CommunityService.COMP_NAME);
m_commService.addLoginListener(this);
// Call login
m_commService.loginByPassword(serverName, userId, password.toCharArray());
}
}
Once the login has completed, the loggedIn() method is called. At this point the UserManager is instantiated, i.e. the class that handles the list of agents.
Listing 6: the loggedIn() callback.
public void loggedIn(LoginEvent e) {
// Get the support staff names – instantiate the class
m_userManager = new UserManager(m_groupname, m_stsession);
// Indicate that we're ready to receive chats
m_imService = (InstantMessagingService)
m_stsession.getCompApi(InstantMessagingService.COMP_NAME);
m_imService.registerImType(ImTypes.IM_TYPE_CHAT);
m_imService.addImServiceListener(this);
}
The final method of interest is alertPartner(). This method receives the data from the servlet's doPost() and forwards the details to an agent who has been selected by the UserManager. If the bot already has an instant messaging channel open with the agent, it reuses that, otherwise it creates a new one. The data are then sent to the agent in JSON format.
Note that there is no logic present to handle the situation where no agent is found. In this case you could display a message asking the user to try again later, or some other alternative function.
Listing 7: the alertPartner() function.
public void alertPartner(String id, String name, String email,
String phone, String reason) {
String json = "{ \"id\": \"" + id +
"\", \"name\": \"" + name +
"\", \"email\":\"" + email +
"\", \"phone\":\"" + phone +
"\", \"reason\":\"" + reason + "\" }";
STUser agent = (null == m_userManager)? null
: m_userManager.getNextAvailable();
// Found an agent?
if (null != agent) {
Im im = null;
Im currentIm = null;
// See if this agent's IM already exists
for (int iX=0; iX< m_ImOpened.size(); iX++)
{
currentIm = m_ImOpened.elementAt(iX);
if (currentIm.getPartner().equals(agent)) {
im = currentIm;
break;
}
}
// Not found – create a new
if (null == im) {
im = m_imService.createIm(agent, EncLevel.ENC_LEVEL_NONE,
ImTypes.IM_TYPE_CHAT);
m_ImOpened.addElement(im);
im.addImListener(this);
im.open();
}
// Send the message
im.sendText(true, json);
}
}
The Sametime Bot – the UserManager
The UserManager class looks after the agents and their availability. It adds the members of the agents group to its watchlist, and tracks their status. To do this requires that it support two of the Sametime functions, LookupService and AwarenessService. In reality what it does is identify the members of the group, storing them in an internal table, and then maintaining the individual user's status information in the table. This is a simple solution but not very dynamic. An alternative solution could be to query the a user's status from the watchlist. This would make the solution much more dynamic, but that's an exercise for you to do later.
While most of the functions in this class are common toolkit functions, the key method we're interested in is getNextAvailable(). The algorithm is very simple, merely selecting the first available agent. This is an area where your own requirements will dictate the algorithm you use. This particular solution will put most of the load on the users who come first in the list, but you may want to ensure an more equitable share of the workload, or use some other criteria to determine who gets selected and when.
Listing 8: Select an agent
public STUser getNextAvailable() {
for (int iX=0; iX<m_supportstaff.size(); iX++) {
STWatchedUser user = m_supportstaff.get(iX);
// User's status “Available” = able to chat to customer
if (user.getStatus().isStatus(STUserStatus.ST_USER_STATUS_ACTIVE)) {
log.fine(proc + "- Found: " + user.getDisplayName());
return user;
}
}
// None available
return null;
}
Testing the servlet
Of course you'll want to be certain that your code is working as required. The attached sample code has the correct structure to allow you to create a simple WAR to deploy the servlet, although you need to add the stcommsrvrtk.jar from the Sametime server toolkit. It also has a simple web page called testbot.html: replace the string @nowiki@1in this file with the URL of your own server, i.e. where you have deployed the servlet. When you click the Submit button, it should respond with the text “Request received. Locating agent ...”.
For more detailed information, you can enable logging. The sample code has a number of logging messages which can be enabled in the System Console if you have deployed the servlet on Websphere Application Server. If you have deployed it on a simpler test server such as Apache Tomcat, enabling logging is slightly different, e.g. in Tomcat simply add the following line to your logging.properties file and restart the server:
Listing 9: Enabling Apache Tomcat logging.
com.ibm.sametime.sample.level = FINEST
Conclusion
In this first part of the series, we covered how to implement the server-side code, where the bot is the customer's only direct contact with the system, providing a little extra security by blocking direct access to the support function.
The next part of this series will explain how to create the customer and service agent parts of the application.
Creating a customer service web application using Sametime - Part Two
Creating a customer service web application using Sametime - Part Three
Resources
Sametime Standard V8.5.2 SDK IFR 1 Multiplatform:
https://ibm.biz/BdxaNm
developerWorks® IBM Sametime product page:
https://ibm.biz/BdxVZU
About the authors
Brendan Murray
Brendan joined Lotus Development in 1991 and was acquired, along with the rest of the company, by IBM in 1995. Most recently he has been working on Sametime with a particular emphasis on its Web functionality. You can reach him at
brendan_murray@ie.ibm.com
William Holmes
William has been with IBM since 2007 and is currently the Technical Lead for the Sametime Proxy Web client. You can reach him at
HOLMESW@ie.ibm.com